NexusPi Git Node
native/TCPHostInterface.cpp HEAD (592c826d) Text, 8.71 KB
T8b949e// Copyright (C) 2026, Chad Attermann
Tff7b72#Tff7b72include T8b949e"TCPHostInterface.h"
Tff7b72#Tff7b72include T8b949e<arpa/inet.h>
Tff7b72#Tff7b72include T8b949e<cerrno>
Tff7b72#Tff7b72include T8b949e<cstdio>
Tff7b72#Tff7b72include T8b949e<cstring>
Tff7b72#Tff7b72include T8b949e<ctime>
Tff7b72#Tff7b72include T8b949e<fcntl.h>
Tff7b72#Tff7b72include T8b949e<netinet/in.h>
Tff7b72#Tff7b72include T8b949e<netinet/tcp.h>
Tff7b72#Tff7b72include T8b949e<sys/socket.h>
Tff7b72#Tff7b72include T8b949e<unistd.h>
Tff7b72namespace T7ee787native_kiss_tcp Tb4b4b4{
Tff7b72namespace Tb4b4b4{
T8b949e// KISS frame delimiter — used as the "no data" sentinel from read(), so
T8b949e// a host that races us into a drained buffer sees a frame boundary rather
T8b949e// than garbage. Matches Remote.h::wifi_remote_read().
Tff7b72constexpr Tffa657uint8_t Te6edf3KISS_FEND Tff7b72= T79c0ff0xC0Tb4b4b4;
T8b949e// 15 s mirrors WR_READ_TIMEOUT_MS in Remote.h. If we see no inbound bytes
T8b949e// for this long AND no client is generating any traffic, we tear down the
T8b949e// socket so a fresh client can take over without waiting for the kernel's
T8b949e// keepalive timer (which defaults to ~2 hours on Linux).
Tff7b72constexpr Tffa657int Te6edf3IDLE_TIMEOUT_MS Tff7b72= T79c0ff15000Tb4b4b4;
T8b949e// Local 256-byte staging buffer between the kernel socket and the
T8b949e// per-byte FIFO consumer in buffer_serial(). One recv() per fill cycle
T8b949e// instead of one syscall per byte.
Tff7b72constexpr Tffa657size_t Te6edf3RX_BUF_SIZE Tff7b72= T79c0ff256Tb4b4b4;
Tffa657int Te6edf3listen_fd Tff7b72= T79c0ff-1Tb4b4b4;
Tffa657int Te6edf3client_fd Tff7b72= T79c0ff-1Tb4b4b4;
Tffa657uint8_t Te6edf3rx_bufTb4b4b4[Te6edf3RX_BUF_SIZETb4b4b4]Tb4b4b4;
Tffa657size_t Te6edf3rx_head Tff7b72= T79c0ff0Tb4b4b4;
Tffa657size_t Te6edf3rx_tail Tff7b72= T79c0ff0Tb4b4b4;
Tffa657uint32_t Te6edf3last_activity_ms Tff7b72= T79c0ff0Tb4b4b4;
T8b949e// monotonic_ms — Arduino's millis() is available since we link against
T8b949e// Portduino, but we don't want to pull in Arduino.h here. clock_gettime
T8b949e// with CLOCK_MONOTONIC is the POSIX equivalent and matches what we need
T8b949e// for timeouts.
Tffa657uint32_t Td2a8ffnow_msTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72struct T56d364timespec Te6edf3tsTb4b4b4;
Te6edf3clock_gettimeTb4b4b4(Te6edf3CLOCK_MONOTONICTb4b4b4, Tff7b72&Te6edf3tsTb4b4b4)Tb4b4b4;
Tff7b72return Tff7b72static_castTff7b72<Tffa657uint32_tTff7b72>Tb4b4b4(Te6edf3tsTb4b4b4.Te6edf3tv_sec Tff7b72* T79c0ff1000 Tff7b72+ Te6edf3tsTb4b4b4.Te6edf3tv_nsec Tff7b72/ T79c0ff1000000Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tffa657void Td2a8ffset_nonblockingTb4b4b4(Tffa657int Te6edf3fdTb4b4b4) Tb4b4b4{
Tffa657int Te6edf3flags Tff7b72= Te6edf3fcntlTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3F_GETFLTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3flags Tff7b72< T79c0ff0Tb4b4b4) Tff7b72returnTb4b4b4;
Tb4b4b4(Tffa657voidTb4b4b4)Te6edf3fcntlTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3F_SETFLTb4b4b4, Te6edf3flags Tff7b72| Te6edf3O_NONBLOCKTb4b4b4)Tb4b4b4;
Tb4b4b4}
T8b949e// Close-on-exec so a re-exec'd daemon (native_reboot::perform) doesn't
T8b949e// inherit the parent's listener/client and fail to re-bind with EADDRINUSE.
T8b949e// shutdown() also closes these fds explicitly — this is belt and suspenders,
T8b949e// and covers any future exec*() callers too.
Tffa657void Td2a8ffset_cloexecTb4b4b4(Tffa657int Te6edf3fdTb4b4b4) Tb4b4b4{
Tffa657int Te6edf3flags Tff7b72= Te6edf3fcntlTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3F_GETFDTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3flags Tff7b72< T79c0ff0Tb4b4b4) Tff7b72returnTb4b4b4;
Tb4b4b4(Tffa657voidTb4b4b4)Te6edf3fcntlTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3F_SETFDTb4b4b4, Te6edf3flags Tff7b72| Te6edf3FD_CLOEXECTb4b4b4)Tb4b4b4;
Tb4b4b4}
T8b949e// Enable TCP keepalive on the accepted socket with aggressive timing so
T8b949e// the kernel reaps half-open peers within ~10 s of them going silent,
T8b949e// rather than the platform default (~2 hours on Linux, ~2 hours on
T8b949e// macOS). The named knobs differ between Linux and macOS — on Linux
T8b949e// the idle-before-first-probe is TCP_KEEPIDLE, on macOS it's TCP_KEEPALIVE.
Tffa657void Td2a8ffapply_keepaliveTb4b4b4(Tffa657int Te6edf3fdTb4b4b4) Tb4b4b4{
Tffa657int Te6edf3yes Tff7b72= T79c0ff1Tb4b4b4;
Te6edf3setsockoptTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3SOL_SOCKETTb4b4b4, Te6edf3SO_KEEPALIVETb4b4b4, Tff7b72&Te6edf3yesTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3yesTb4b4b4)Tb4b4b4)Tb4b4b4;
Tffa657int Te6edf3idle_s Tff7b72= T79c0ff5Tb4b4b4;
Tffa657int Te6edf3interval_s Tff7b72= T79c0ff2Tb4b4b4;
Tffa657int Te6edf3count Tff7b72= T79c0ff3Tb4b4b4;
Tff7b72#Tff7b72ifdef __APPLE__
Te6edf3setsockoptTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3IPPROTO_TCPTb4b4b4, Te6edf3TCP_KEEPALIVETb4b4b4, Tff7b72&Te6edf3idle_sTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3idle_sTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72else
Te6edf3setsockoptTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3IPPROTO_TCPTb4b4b4, Te6edf3TCP_KEEPIDLETb4b4b4, Tff7b72&Te6edf3idle_sTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3idle_sTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif
Tff7b72#Tff7b72ifdef TCP_KEEPINTVL
Te6edf3setsockoptTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3IPPROTO_TCPTb4b4b4, Te6edf3TCP_KEEPINTVLTb4b4b4, Tff7b72&Te6edf3interval_sTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3interval_sTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif
Tff7b72#Tff7b72ifdef TCP_KEEPCNT
Te6edf3setsockoptTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3IPPROTO_TCPTb4b4b4, Te6edf3TCP_KEEPCNTTb4b4b4, Tff7b72&Te6edf3countTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3countTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif
Tb4b4b4}
Tffa657void Td2a8ffclose_clientTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3client_fd Tff7b72>Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Tff7b72:Tff7b72:Te6edf3closeTb4b4b4(Te6edf3client_fdTb4b4b4)Tb4b4b4;
Te6edf3client_fd Tff7b72= T79c0ff-1Tb4b4b4;
Tb4b4b4}
Te6edf3rx_head Tff7b72= Te6edf3rx_tail Tff7b72= T79c0ff0Tb4b4b4;
Tb4b4b4}
T8b949e// Drain whatever the kernel has queued on the client socket into the
T8b949e// staging buffer. Treats clean close (recv == 0) and hard errors as
T8b949e// "client gone", so the next poll_accept() will pick up a new one.
Tffa657void Td2a8fffill_rx_bufferTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3client_fd Tff7b72< T79c0ff0Tb4b4b4) Tff7b72returnTb4b4b4;
T8b949e// Reset the buffer if it's empty so we always read into the front
T8b949e// (avoids needing a ring).
Tff7b72if Tb4b4b4(Te6edf3rx_head Tff7b72=Tff7b72= Te6edf3rx_tailTb4b4b4) Tb4b4b4{
Te6edf3rx_head Tff7b72= Te6edf3rx_tail Tff7b72= T79c0ff0Tb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3rx_tail Tff7b72>Tff7b72= Te6edf3RX_BUF_SIZETb4b4b4) Tff7b72returnTb4b4b4; T8b949e// full, consumer must drain first
Tffa657ssize_t Te6edf3n Tff7b72= Tff7b72:Tff7b72:Te6edf3recvTb4b4b4(Te6edf3client_fdTb4b4b4, Te6edf3rx_buf Tff7b72+ Te6edf3rx_tailTb4b4b4, Te6edf3RX_BUF_SIZE Tff7b72- Te6edf3rx_tailTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3n Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3rx_tail Tff7b72+Tff7b72= Tff7b72static_castTff7b72<Tffa657size_tTff7b72>Tb4b4b4(Te6edf3nTb4b4b4)Tb4b4b4;
Te6edf3last_activity_ms Tff7b72= Te6edf3now_msTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3n Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
T8b949e// Peer closed cleanly.
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] client disconnectedTffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Te6edf3close_clientTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3errno Tff7b72!Tff7b72= Te6edf3EAGAIN Tff7b72&Tff7b72& Te6edf3errno Tff7b72!Tff7b72= Te6edf3EWOULDBLOCK Tff7b72&Tff7b72& Te6edf3errno Tff7b72!Tff7b72= Te6edf3EINTRTb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] recv error: %s — dropping clientTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Te6edf3close_clientTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tb4b4b4} T8b949e// namespace
Tffa657bool Te6edf3initTb4b4b4(Tffa657uint16_t Te6edf3portTb4b4b4, Tffa657bool Te6edf3bind_publicTb4b4b4) Tb4b4b4{
Te6edf3listen_fd Tff7b72= Tff7b72:Tff7b72:Te6edf3socketTb4b4b4(Te6edf3AF_INETTb4b4b4, Te6edf3SOCK_STREAMTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3listen_fd Tff7b72< T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] socket(): %sTffea00\nTa5d6ff"Tb4b4b4, Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72return Tffa657falseTb4b4b4;
Tb4b4b4}
Tffa657int Te6edf3yes Tff7b72= T79c0ff1Tb4b4b4;
Te6edf3setsockoptTb4b4b4(Te6edf3listen_fdTb4b4b4, Te6edf3SOL_SOCKETTb4b4b4, Te6edf3SO_REUSEADDRTb4b4b4, Tff7b72&Te6edf3yesTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3yesTb4b4b4)Tb4b4b4)Tb4b4b4;
T8b949e// Loopback by default; bind_public flips to 0.0.0.0 so remote hosts
T8b949e// on the same network can connect. No auth/encryption either way —
T8b949e// public binding is an opt-in for trusted networks.
Tff7b72const Tffa657uint32_t Te6edf3bind_addr Tff7b72= Te6edf3bind_public Tff7b72? Te6edf3INADDR_ANY Tff7b72: Te6edf3INADDR_LOOPBACKTb4b4b4;
Tff7b72const Tffa657charTff7b72* Te6edf3bind_label Tff7b72= Te6edf3bind_public Tff7b72? Ta5d6ff"Ta5d6ff0.0.0.0Ta5d6ff" Tff7b72: Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4;
Te6edf3sockaddr_in Te6edf3addrTb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3memsetTb4b4b4(Tff7b72&Te6edf3addrTb4b4b4, T79c0ff0Tb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3addrTb4b4b4)Tb4b4b4)Tb4b4b4;
Te6edf3addrTb4b4b4.Te6edf3sin_family Tff7b72= Te6edf3AF_INETTb4b4b4;
Te6edf3addrTb4b4b4.Te6edf3sin_addrTb4b4b4.Te6edf3s_addr Tff7b72= Te6edf3htonlTb4b4b4(Te6edf3bind_addrTb4b4b4)Tb4b4b4;
Te6edf3addrTb4b4b4.Te6edf3sin_port Tff7b72= Te6edf3htonsTb4b4b4(Te6edf3portTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3bindTb4b4b4(Te6edf3listen_fdTb4b4b4, Tff7b72reinterpret_castTff7b72<Te6edf3sockaddrTff7b72*Tff7b72>Tb4b4b4(Tff7b72&Te6edf3addrTb4b4b4)Tb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3addrTb4b4b4)Tb4b4b4) Tff7b72< T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] bind(%s:%u): %sTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3bind_labelTb4b4b4, Te6edf3portTb4b4b4, Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72:Tff7b72:Te6edf3closeTb4b4b4(Te6edf3listen_fdTb4b4b4)Tb4b4b4;
Te6edf3listen_fd Tff7b72= T79c0ff-1Tb4b4b4;
Tff7b72return Tffa657falseTb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3listenTb4b4b4(Te6edf3listen_fdTb4b4b4, T79c0ff1Tb4b4b4) Tff7b72< T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] listen(): %sTffea00\nTa5d6ff"Tb4b4b4, Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72:Tff7b72:Te6edf3closeTb4b4b4(Te6edf3listen_fdTb4b4b4)Tb4b4b4;
Te6edf3listen_fd Tff7b72= T79c0ff-1Tb4b4b4;
Tff7b72return Tffa657falseTb4b4b4;
Tb4b4b4}
Te6edf3set_nonblockingTb4b4b4(Te6edf3listen_fdTb4b4b4)Tb4b4b4;
Te6edf3set_cloexecTb4b4b4(Te6edf3listen_fdTb4b4b4)Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] listening on %s:%u%sTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3bind_labelTb4b4b4, Te6edf3portTb4b4b4, Te6edf3bind_public Tff7b72? Ta5d6ff"Ta5d6ff (PUBLIC — no auth)Ta5d6ff" Tff7b72: Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4;
Tff7b72return Tffa657trueTb4b4b4;
Tb4b4b4}
Tffa657void Te6edf3poll_acceptTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3listen_fd Tff7b72< T79c0ff0Tb4b4b4) Tff7b72returnTb4b4b4;
T8b949e// Drain pending accepts. Either we adopt one (no client yet) or we
T8b949e// immediately close any extras (one already attached).
Tff7b72while Tb4b4b4(Tffa657trueTb4b4b4) Tb4b4b4{
Te6edf3sockaddr_in Te6edf3peerTb4b4b4;
Tffa657socklen_t Te6edf3plen Tff7b72= Tff7b72sizeofTb4b4b4(Te6edf3peerTb4b4b4)Tb4b4b4;
Tffa657int Te6edf3fd Tff7b72= Tff7b72:Tff7b72:Te6edf3acceptTb4b4b4(Te6edf3listen_fdTb4b4b4, Tff7b72reinterpret_castTff7b72<Te6edf3sockaddrTff7b72*Tff7b72>Tb4b4b4(Tff7b72&Te6edf3peerTb4b4b4)Tb4b4b4, Tff7b72&Te6edf3plenTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3fd Tff7b72< T79c0ff0Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3errno Tff7b72!Tff7b72= Te6edf3EAGAIN Tff7b72&Tff7b72& Te6edf3errno Tff7b72!Tff7b72= Te6edf3EWOULDBLOCK Tff7b72&Tff7b72& Te6edf3errno Tff7b72!Tff7b72= Te6edf3EINTRTb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] accept(): %sTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tff7b72breakTb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3client_fd Tff7b72>Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
T8b949e// Already attached. Reject the second client immediately.
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4,
Ta5d6ff"Ta5d6ff[kiss-tcp] rejecting second client (one already attached)Tffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Tff7b72:Tff7b72:Te6edf3closeTb4b4b4(Te6edf3fdTb4b4b4)Tb4b4b4;
Tff7b72continueTb4b4b4;
Tb4b4b4}
Te6edf3set_nonblockingTb4b4b4(Te6edf3fdTb4b4b4)Tb4b4b4;
Te6edf3set_cloexecTb4b4b4(Te6edf3fdTb4b4b4)Tb4b4b4;
Te6edf3apply_keepaliveTb4b4b4(Te6edf3fdTb4b4b4)Tb4b4b4;
T8b949e// Nagle off — KISS frames are small and latency-sensitive.
Tffa657int Te6edf3yes Tff7b72= T79c0ff1Tb4b4b4;
Te6edf3setsockoptTb4b4b4(Te6edf3fdTb4b4b4, Te6edf3IPPROTO_TCPTb4b4b4, Te6edf3TCP_NODELAYTb4b4b4, Tff7b72&Te6edf3yesTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3yesTb4b4b4)Tb4b4b4)Tb4b4b4;
Te6edf3client_fd Tff7b72= Te6edf3fdTb4b4b4;
Te6edf3rx_head Tff7b72= Te6edf3rx_tail Tff7b72= T79c0ff0Tb4b4b4;
Te6edf3last_activity_ms Tff7b72= Te6edf3now_msTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] client connectedTffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Tb4b4b4}
T8b949e// Pull whatever the kernel has buffered for the active client.
Te6edf3fill_rx_bufferTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tffa657void Te6edf3check_activeTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3client_fd Tff7b72< T79c0ff0Tb4b4b4) Tff7b72returnTb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3now_msTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3last_activity_ms Tff7b72>Tff7b72= Tff7b72static_castTff7b72<Tffa657uint32_tTff7b72>Tb4b4b4(Te6edf3IDLE_TIMEOUT_MSTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] client idle %d ms — closingTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3IDLE_TIMEOUT_MSTb4b4b4)Tb4b4b4;
Te6edf3close_clientTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tffa657bool Te6edf3is_connectedTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3client_fd Tff7b72>Tff7b72= T79c0ff0Tb4b4b4;
Tb4b4b4}
Tffa657bool Te6edf3availableTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3client_fd Tff7b72>Tff7b72= T79c0ff0 Tff7b72&Tff7b72& Te6edf3rx_head Tff7b72< Te6edf3rx_tailTb4b4b4;
Tb4b4b4}
Tffa657uint8_t Te6edf3readTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3rx_head Tff7b72< Te6edf3rx_tailTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3rx_bufTb4b4b4[Te6edf3rx_headTff7b72+Tff7b72+Tb4b4b4]Tb4b4b4;
Tb4b4b4}
Tff7b72return Te6edf3KISS_FENDTb4b4b4;
Tb4b4b4}
Tffa657void Te6edf3writeTb4b4b4(Tffa657uint8_t Te6edf3byteTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3client_fd Tff7b72< T79c0ff0Tb4b4b4) Tff7b72returnTb4b4b4;
Tffa657ssize_t Te6edf3n Tff7b72= Tff7b72:Tff7b72:Te6edf3sendTb4b4b4(Te6edf3client_fdTb4b4b4, Tff7b72&Te6edf3byteTb4b4b4, T79c0ff1Tb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3n Tff7b72=Tff7b72= T79c0ff1Tb4b4b4) Tb4b4b4{
Te6edf3last_activity_ms Tff7b72= Te6edf3now_msTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72returnTb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3n Tff7b72< T79c0ff0 Tff7b72&Tff7b72& Tb4b4b4(Te6edf3errno Tff7b72=Tff7b72= Te6edf3EAGAIN Tff7b72|Tff7b72| Te6edf3errno Tff7b72=Tff7b72= Te6edf3EWOULDBLOCK Tff7b72|Tff7b72| Te6edf3errno Tff7b72=Tff7b72= Te6edf3EINTRTb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// The kernel's send buffer is full — for our low byte rate this
T8b949e// shouldn't happen with a healthy client. Treat it as the client
T8b949e// failing to drain and drop them so we don't hang on subsequent
T8b949e// writes.
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] send would block — dropping clientTffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[kiss-tcp] send error: %s — dropping clientTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3close_clientTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tffa657void Te6edf3shutdownTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3close_clientTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3listen_fd Tff7b72>Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Tff7b72:Tff7b72:Te6edf3closeTb4b4b4(Te6edf3listen_fdTb4b4b4)Tb4b4b4;
Te6edf3listen_fd Tff7b72= T79c0ff-1Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tb4b4b4} T8b949e// namespace native_kiss_tcp
Served by rngit 1.5.2 - Generated in 0.05s